爬取点评成都美食后排序

爬取大众点评成都地区餐厅数据,然后根据各个维度特征进行排序

爬虫

代码传送门

第三方包介绍

  • requests - http客户端
  • bs4 - 解析html文件
  • pandas - 处理和保存数据

构造url

url按照菜系码+页数组成,每个菜系最多50页

for k, v in FOOD_C.items():
    print(f"爬{k}ing......")
    for num in range(1, 51):
        url = f"{CHENGDU}/{v}p{num}"
        self._page_parse(url, k, results)

解析页面中需要的特征,包括: 等级,价格,评论数,味道,环境,服务

def _parse_infos(self, shop, foodtype):
    if not self._is_spide(shop):
        return None

    name = shop.find("h4").renderContents().decode()
    info = shop.find(
        "a", {"data-click-name": "shop_title_click"})

    _id = info["data-shopid"]
    url = info["href"]

    price = shop.find("a", {"class": "mean-price"}
                      ).renderContents().decode()
    price = re.search('¥(.+)<', price)
    if price is None:
        price = -1
    else:
        price = price.group(1)

    comment_list = shop.find(
        "span", {"class": "comment-list"})

    comments = comment_list.find_all("b")
    comments = self.comments._make(
        [c.renderContents().decode() for c in comments])

    review = shop.find("a", {"class": "review-num"}
                       )
    if review is None:
        review = -1
    else:
        review = review.b.renderContents().decode()

    level = shop.find("span", {"class": "sml-rank-stars"})
    if level is None:
        level = "无"
    else:
        level = level["title"]

    result = self.shop_info._make(
        [foodtype, name, _id, url, level, price, comments, review])
    return result

结果

手动打个码 :)

,foodtype,name,id,url,level,price,taste,envi,service,review
0,火锅,***,***,http://www.dianping.com/shop/***,五星商户,78,8.9,9.1,9.0,1066
1,火锅,***,***,http://www.dianping.com/shop/***,五星商户,137,9.0,8.9,9.0,4280
2,火锅,***,***,http://www.dianping.com/shop/***,五星商户,105,9.0,9.2,9.0,1629
3,火锅,***,***,http://www.dianping.com/shop/***,准五星商户,148,8.2,9.2,8.7,2839
4,火锅,***,***,http://www.dianping.com/shop/***,准五星商户,92,8.5,8.8,8.9,175
5,火锅,***,***,http://www.dianping.com/shop/***,四星商户,65,7.8,7.8,7.8,414

数据分析

第三方包介绍

  • numpy - 科学计算
  • sklearn - 仅仅使用了归一化函数MinMaxScaler
  • pandas - 处理和保存数据

初始化

将dz的自有等级转换为float 可能同一家餐厅出现在多个菜系中,所以根据id去重

def __init__(self,
             norml="mms",
             inf="../shops_info.csv"):
    """
    参数:
        columns - 排序列,类型:list 可以有多个成员
        foodc - 菜系,默认为所有餐厅
    """

    self.df = pd.read_csv(inf)
    self.df["level"] = self.df["level"].apply(lambda x: LEVEL[x])  # 转换格式
    self.df = self.df.drop_duplicates(["id"])  # 去重

    if norml == "mms":
        self._mm_normal()
    elif norml == "std":
        self._std_normal()
    else:
        raise Exception("目前支持mms和std")

归一化

  • 方式1:公式 (x - x_mean) / x_std
def _std_normal(self):
    # 归一化
    def norml(x):
        std = x.std()
        mean = x.mean()
        return x.apply(lambda x: (x - mean) / std)

    self.df["level_std"] = norml(self.df["level"])
    self.df["taste_std"] = norml(self.df["taste"])
    self.df["envi_std"] = norml(self.df["envi"])
    self.df["service_std"] = norml(self.df["service"])
    self.df["review_std"] = norml(self.df["review"])

    # 先乘以权重再累加
    self.df["sissi_std"] = self.df.apply(lambda x: sum(
        [x[k + "_std"] * v for k, v in SISSISTD.items()]), axis=1)
  • 方式2:公式 (x - x.min) / (x.max - X.min)
def _mm_normal(self):
    filter_list = ["level", "taste", "envi", "service", "review"]
    df = self.df.filter(items=filter_list)
    data = MinMaxScaler().fit_transform(df)

    df = pd.DataFrame(data, columns=filter_list)

    self.df["sissi_std"] = df.apply(lambda x: sum(
        [x[k] * v for k, v in SISSISTD.items()]), axis=1)

预测

def _rank_by_foodc(self, columns, foodc):
    """
    参数:
        columns - 排序列,类型:list 可以有多个成员
        foodc - 菜系,默认为所有餐厅
    """

    if foodc == "all":
        temp = self.df
    elif foodc in FOOD_C.keys():
        temp = self.df[self.df["foodtype"] == foodc]
    else:
        raise Exception(f"没有当前菜系 - {foodc}")

    return temp.sort_values(by=columns, ascending=False)

def divine(self, columns,
           foodc="all",
           out_nums=40,
           show_names=True,
           show_pic=False):
    """
    按需展示排序结果,画图会出现中文乱码问题,解决办法请google :)
    参数:
        columns - 排序列,类型:list 可以有多个成员
        foodc - 菜系
        out_nums - 输出结果数
        show_names - 为True时显示餐厅名字
        show_pic - 为True时显示餐厅柱状图,仅仅在mac上可用,linux和windows不管
    """

    ranked = self._rank_by_foodc(columns, foodc).head(out_nums)

    names = ranked["name"]
    stds = ranked["sissi_std"]
    tastes = ranked["taste"]
    envis = ranked["envi"]
    # services = ranked["service"]
    reviews = ranked["review"]

    if show_names:
        print(f"筛选得到餐厅:\n{names}")

    if show_pic:
        import matplotlib.pyplot as plt
        plt.rcParams['font.sans-serif'] = ['SimHei']
        plt.rcParams['axes.unicode_minus'] = False

        fig, ax = plt.subplots()
        n_groups = len(ranked)
        index = np.arange(n_groups)

        bar_width = 0.1
        opacity = 0.4
        error_config = {'ecolor': '0.3'}

        ax.bar(index, stds, bar_width, alpha=opacity,
               color='b', error_kw=error_config, label=u'标准')

        ax.bar(index + bar_width, tastes, bar_width, alpha=opacity,
               color='r', error_kw=error_config, label='口味')

        ax.bar(index + 2 * bar_width, envis, bar_width, alpha=opacity,
               color='g', error_kw=error_config, label='服务')

        ax.set_xlabel(foodc)
        ax.set_ylabel('Scores')
        ax.set_title('{}-top{}'.format(foodc, n_groups))
        ax.set_xticks(index + bar_width / 2)
        ax.set_xticklabels(names, fontsize=8)
        ax.legend()

        fig.tight_layout()
        plt.show()

结果

筛选得到餐厅:
7781          清漫香草海鲜火锅(保利198店)
7788          青柠记忆泰式海鲜火锅(优品道店)
7780         曼忆莲·泰式海鲜火锅(武侯万达店)
7795            阿诺泰·泰国菜餐厅(光华店)
7789             Breeze In微风小馆
7802                  萌新儿越南pho
7792         GOGO Plate(环球中心店)
7784            卷恋越南料理(凯德来福士店)
7787                    心悦轩泰餐厅
7786            清迈泰式海鲜火锅(铁像寺店)
7867                      印度甩饼
7798                  可美马来西亚餐厅
7866                  咖喱屋(北辰店)
7799    青柠叶·泰式冬阴功海鲜火锅(龙湖北城天街店)
7804            叁月拾二泰餐厅(东能财富店)
7828                      泰色天香
7783           万象·泰式海鲜火锅(创意山店)
7863               泰龙风·纯正东南亚味道
7797                    泰悦泰式料理
7875                        茗苑
7793                  6号泰式海鲜火锅
7864                      灵魂咖喱
7803                      十里春风
7791       泰柠小馆泰式料理·火锅(伊藤洋华堂店)
7801            苏梅泰式海鲜火锅(都江堰店)
7868                  稻米香冻粑叶儿粑
7800               辛格印度餐厅(新都店)
7794             青葵泰式海鲜火锅(郫县店)
7870                    力泰亚洲小厨
7807                小象花园泰式海鲜火锅
7805              泰Basil泰式特色餐厅
7860                    马六甲宴会厅
7811                泰Basil泰国餐厅
7748           泰香米泰国餐厅(春熙路群光店)
7809                 你好北柳泰国传统菜
7833                  泰喜欢(环球店)
7865                       特味轩
7872            新加坡业园区2站自行车租赁点
7790                    泰乙泰国餐厅
7869                      印巴精舍
Name: name, dtype: object
Nevermore Written by:

步步生姿,空锁满庭花雨。胜将娇花比。